﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Win32Import;
using System.Xml;
using System.Xml.Schema;
using System.Windows.Forms.Integration;
using System.Windows.Controls;

namespace FennecUI
{
    public partial class MainForm : TouchForm
    {

        private Point TouchStart;       //  initial touch point, set on touch down
        private Point TouchOffset;      //  the relative window position on touch down
        private Point WindowPosition;   //  current position of the window group
        private Point Target;           //  target position of the window group

        private AppletCommunicationBus appletComBus;

        private bool FullMode;
        private Applet FullModeApplet;

        private List<Applet> Applets;       // List of all applet windows
        private List<AppletConfiguration> InstalledApplets;  // List of all installed applets
        private List<AppletLauncher> Launchers; // List of applet launcher objects

        public MainForm()
        {
            InitializeComponent();
            //TopMost = true;

            Applets = new List<Applet>();
            Launchers = new List<AppletLauncher>();

            appletComBus = new AppletCommunicationBus();
            appletComBus.onCommandReceived += new AppletCommunicationBus.CommandReceived(appletComBus_onCommandReceived);

            // Initialize touch animation variables
            WindowPosition = new Point(40, 80);   
            Target = new Point(40, 80);           
            TouchStart = new Point(0, 0);       
            TouchOffset = new Point(0, 0);

            FullMode = false;

            // Touch register touch events
            Touchdown += new EventHandler<WMTouchEventArgs>(onTouchdown);
            TouchMove += new EventHandler<WMTouchEventArgs>(onTouchMove);

            // Setup the animation timer (redraws the form)
            Timer animationTimer = new Timer();
            animationTimer.Interval = 40;
            animationTimer.Tick += new EventHandler(onAnimationTimerTick);
            animationTimer.Start();

            Load += new EventHandler(onLoad);
        }

        private delegate void LaunchAppletCallback(AppletConfiguration config);

        void appletComBus_onCommandReceived(Guid senderGuid, byte senderInstance, MessageType messageType, string message)
        {
            switch (messageType)
            {
                case MessageType.EnterFullMode:
                    break;
                case MessageType.OpenApplet:
                    break;
                case MessageType.OpenAppletAdvanced:
                    break;
                case MessageType.OpenFile:
                    bool handled=false;
                    string extension = message.Substring(message.LastIndexOf(".") + 1);
                    foreach (AppletConfiguration config in InstalledApplets)
                    {
                        if(config.FileHandlers.Contains(extension)){
                            handled=true;
                            AppletConfiguration startConfig=config;
                            startConfig.StartParameters=message;
                            LaunchAppletCallback launchCallback = new LaunchAppletCallback(launchApplet);
                            Invoke(launchCallback,new object[]{startConfig});
                        }
                    }
                    if (!handled)
                    {
                        ProcessStartInfo pinfo = new ProcessStartInfo(message);
                        pinfo.UseShellExecute = true;
                        Process.Start(pinfo);
                    }
                    break;
            }
        }

        // Loads applets from configuration file and adds applet launchers
        private void onLoad(object sender, EventArgs e)
        {

           launcherPanel1.Height = (int)(0.3 * Bounds.Height-120);
           launcherPanel1.Top = (int)(0.7 * Bounds.Height+120);

            InstalledApplets=loadApplets(Configuration.APPLET_CONFIGURATION_FILE); // Load list of applets from XML

            foreach (AppletConfiguration appletConfig in InstalledApplets)
            {
                AppletLauncher launcher = new AppletLauncher(appletConfig);
                launcher.Margin = new System.Windows.Thickness(0,10,0,10);
                Grid.SetColumn(launcher, launcherPanelWPF.content.ColumnDefinitions.Count);
                ColumnDefinition col = new ColumnDefinition();
                col.Width = new System.Windows.GridLength(140);
                launcherPanelWPF.content.ColumnDefinitions.Add(col);
                launcherPanelWPF.content.Children.Add(launcher);
                Launchers.Add(launcher);
                launcher.MouseUp += new System.Windows.Input.MouseButtonEventHandler(launcher_MouseUp);
            }
        }

        void launcher_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            AppletConfiguration config = ((AppletLauncher)sender).Configuration;
            launchApplet(config);
        }

        void launchApplet(AppletConfiguration config)
        {
            if (FullMode)
            {
                FullModeApplet.toggleFullMode();
            }
            if (config.SingleInstance)  // If Applet is a single instance check if it is already open then focus on it and return
            {
                foreach (Applet a in Applets)
                {
                    if (config.AppletID == a.Configuration.AppletID)
                    {
                        Target.X = (int)(0.15 * Bounds.Width) - a.StaticPosition;
                        return;
                    }
                }
            }

            addApplet(config); // If hasn't already returned from above launch a new instance of the Applet

            layoutApplets(); // perform a layout pass on the applets
        }

        // Fired when an applet launcher is clicked
        void launcher_Click(object sender, EventArgs e)
        {
            
        }

        // Fired when the applet is requesting to leave full mode
        void applet_LeaveFullMode(Applet sender)
        {
            SuspendLayout();
            FullMode = false;
            sender.Size = new Size((int)(0.7 * Bounds.Width), (int)(0.7*Bounds.Height));
            Target.X = (int)(0.15 * Bounds.Width) - sender.StaticPosition;
            WindowPosition.X = Target.X;
            layoutApplets();
            ResumeLayout();
            FullModeApplet = null;
        }

        // Fired when the applet is requesting to enter full mode
        void applet_FullMode(Applet sender)
        {
            SuspendLayout();
            FullMode = true;
            sender.Location = new Point(0,40);
            sender.Size = new Size(Width, Height - 40);
            sender.BringToFront();
            FullModeApplet = sender;
            ResumeLayout();
        }

        // Fired when the applet has closed
        void applet_Close(Applet sender)
        {
            if (sender == FullModeApplet)
            {
                FullModeApplet = null;
                FullMode = false;
            }
            Applets.Remove(sender);
            Controls.Remove(sender);

            //Reposition the other applets
            for (int i = 0; i < Applets.Count; i++)
            {
                Applets[i].StaticPosition = (int)(0.7 * Bounds.Width + 40) * i;
            }

            // Check if the remaining applets are still on the screen (only relevant for when first/last applets are removed)
            if (Target.X > Bounds.Width - 80 - 0.7 * Bounds.Width)
            {
                Target.X = (int)(Bounds.Width - 80 - 0.7 * Bounds.Width);
            }
            else if (Target.X < (Applets.Count - 1) * (-0.7 * Bounds.Width) + 40)
            {
                Target.X = (int)((Applets.Count - 1) * (-0.7 * Bounds.Width) + 40);
            }

            layoutApplets();
            sender.Dispose();
        }

        // Creates a new applet object from an applet configuration
        public bool addApplet(AppletConfiguration appletConfig)
        {
            Applet applet;
            switch (appletConfig.Type)      //  Generate new applet based on applet type
            {
                case AppletType.GenericNative:
                    applet = new NativeApplet(appletConfig);
                    break;
                case AppletType.WebApplet:
                case AppletType.NativeExtended:
                    applet = new WebApplet(appletConfig);
                    break;
                default:
                    MessageBox.Show("Incompatible Applet");
                    return false;
            }

            applet.Width = (int)(0.7*Bounds.Width);
            applet.Height = (int)(0.7 * Bounds.Height);
            applet.StaticPosition = (int)(0.7 * Bounds.Width + 40) * Applets.Count;
            applet.Location = new Point(applet.StaticPosition + WindowPosition.X, WindowPosition.Y);

            applet.StartApplet();

            BringToFront();

            Controls.Add(applet);

            Applets.Add(applet);
            applet.Close += new Applet.AppletEvent(applet_Close);
            applet.EnterFullMode += new Applet.AppletEvent(applet_FullMode);
            applet.LeaveFullMode += new Applet.AppletEvent(applet_LeaveFullMode);
            
            Target.X = (int)(0.15 * Bounds.Width) - applet.StaticPosition;  // Focus on the applet

            return true;
        }

        private void onAnimationTimerTick(object sender, EventArgs e)
        {
            if (!FullMode)
            {
                // Set window position such that it is 30% of the distance between its current position and the target position
                // By varying this percentage the motion can be made to look smoother and more elastic, though may make the interface seem to lag
                WindowPosition.X = (int)(WindowPosition.X + 0.3 * (Target.X - WindowPosition.X));
                layoutApplets();
                
            }
            else
            {
                WindowPosition.X = Target.X;
            }
        }

        /// <summary>
        /// Layout the applets, applet positions are animated so that when an applet is removed the other applets will animate smoothly to the new position
        /// </summary>
        private void layoutApplets()
        {
            SuspendLayout();
            for (int i = 0; i < Applets.Count; i++)
            {
                int targetPos = (int)(WindowPosition.X + Applets[i].StaticPosition+0.8*(Applets[i].Location.X-Applets[i].StaticPosition-WindowPosition.X));
                Applets[i].Location = new Point(targetPos, WindowPosition.Y);
            }
            ResumeLayout();
        }

        // Handle Touch Events
        private void onTouchdown(object sender, TouchForm.WMTouchEventArgs e)
        {
            if (!FullMode)
            {
                TouchStart.X = e.LocationX;
                TouchStart.Y = e.LocationY;
                TouchOffset.X = WindowPosition.X;
                TouchOffset.Y = WindowPosition.Y;
            }
        }

        private void onTouchMove(object sender, TouchForm.WMTouchEventArgs e)
        {
            if (!FullMode)
            {
                // Target Position = Initial Position + Change in Touch Position
                Target.X = TouchOffset.X + e.LocationX - TouchStart.X;
                Target.Y = TouchOffset.Y + e.LocationY - TouchStart.Y;
                if (Target.X > Bounds.Width - 80 - 0.7 * Bounds.Width)
                {
                    Target.X = (int)(Bounds.Width - 80 - 0.7 * Bounds.Width);
                }
                else if (Target.X < (Applets.Count-1) * (-0.7 * Bounds.Width)+80)
                {
                    Target.X = (int)((Applets.Count-1) * (-0.7 * Bounds.Width)+80);
                }
            }
        }

        /// <summary>
        /// Load Applets from a specified xml file
        /// </summary>
        /// <param name="xmlFile">File to load the xml file from</param>
        /// <returns>A List containing AppletConfiguration objects</returns>
        private List<AppletConfiguration> loadApplets(string xmlFile)
        {
            List<AppletConfiguration> applets = new List<AppletConfiguration>();

            //  XML validated against schema to ensure that it is the correct format
            XmlReaderSettings xrd = new XmlReaderSettings();
            xrd.Schemas.Add("http://code.google.com/p/touchswipe-dev/wiki/FennecAppletSchema", "FennecApplet.xsd");
            xrd.Schemas.Compile();
            xrd.ValidationType = ValidationType.Schema;

            //  Set up the xml parser
            XmlDocument xmlDoc = new XmlDocument();
            XmlReader xmlParser = XmlReader.Create(xmlFile,xrd);
            xmlDoc.Load(xmlParser);
           
            //  Check that the xml document is valid against the schema
            if (xmlDoc.DocumentElement.SchemaInfo.Validity == XmlSchemaValidity.Valid)
            {
                //  Loop over all the children of the <Applets> Element
                for (var n = 0; n < xmlDoc.DocumentElement.ChildNodes.Count; n++)
                {
                    if (xmlDoc.DocumentElement.ChildNodes[n].Name == "Applet")
                    {
                        applets.Add(AppletConfiguration.FromXML(xmlDoc.DocumentElement.ChildNodes[n]));
                    }
                }
                return applets;
            }
            else
            {
                MessageBox.Show("Invalid XML: XML could not be validated against schema");
                return null;
            }
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.LWin || e.KeyCode == Keys.RWin)
            {
                WindowState = FormWindowState.Minimized;
            }
        }

    }
}
